package com.yinxing.framework.exception;

import com.yinxing.framework.domain.R;
import com.yinxing.framework.enums.RCode;
import com.yinxing.framework.shiro.exception.RequiresAuthenticationException;
import com.yinxing.framework.shiro.exception.RequiresUserException;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.ShiroException;
import org.springframework.transaction.CannotCreateTransactionException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.multipart.support.MissingServletRequestPartException;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.servlet.http.HttpServletRequest;
import java.util.List;

@Slf4j
@RestControllerAdvice
public class ExceptionController {

    /**
     * Shiro异常处理.未登录异常
     * 以下filter或者注释拦截到都会抛出此异常
     * RequiresUserFilter,RequiresAuthenticationFilter, @RequiresUser, @RequiresAuthentication
     */
    @ExceptionHandler({ShiroException.class})
    public R handleException(ShiroException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        String errorMsg = String.format("当前URL[%s]拒绝访问.%s", requestURI, e.getMessage());

        //Log
        if(log.isWarnEnabled()) {
            log.warn(errorMsg);
        }

        //用户未认证
        if(e instanceof RequiresAuthenticationException || e instanceof RequiresUserException) {
            return R.err(errorMsg, RCode.ACCOUNT_UNAUTHENTICATED);
        }

        //角色或权限不足
        return R.err(errorMsg, RCode.ACCESS_UNAUTHORIZED);

    }

    /**
     * 请求方式不支持
     */
    @ExceptionHandler({HttpRequestMethodNotSupportedException.class})
    public R handleException(HttpRequestMethodNotSupportedException e, HttpServletRequest request){
        String requestURI = request.getRequestURI();
        String msg = String.format("当前URL[%s]拒绝访问.不支持%s请求", requestURI, e.getMethod());
        log.warn(msg);
        return R.err(msg, RCode.METHOD_NOT_SUPPORTED);
    }

    /**
     * 业务异常
     */
    @ExceptionHandler(value = {YinXingException.class})
    public R handleException(YinXingException e) {
        log.debug(e.getMessage());
        return R.err(e.getMessage(), RCode.ERROR);
    }

    /**
     * 参数不合法
     */
    @ExceptionHandler(value = {ParameterException.class})
    public R handleException(ParameterException e) {
        return R.err(e.getMessage(), RCode.PARAMETER_ILLEGAL);
    }

    /**
     * 参数验证异常处理
     */
    @ExceptionHandler(value = BindException.class)
    public Object handleException(BindException e) {
        List<FieldError> errors = e.getBindingResult().getFieldErrors();
        StringBuffer errorMsg = new StringBuffer();
        errors.stream().forEach(x -> errorMsg.append(x.getDefaultMessage()).append(";"));
        return R.err(errorMsg.toString(), RCode.PARAMETER_ILLEGAL);
    }

    /**
     * 数据库异常
     */
    @ExceptionHandler({CannotCreateTransactionException.class})
    public R exception(CannotCreateTransactionException e) {
        log.error(e.getMessage(), e);
        return R.err("数据库不可用", RCode.DATASOURCE_ERROR);
    }

    /**
     * 路径不存在
     */
    @ExceptionHandler({NoHandlerFoundException.class})
    public R exception(NoHandlerFoundException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        String msg = String.format("当前URL[%s]不存在(404)", requestURI);
        return R.err(msg, RCode.NO_HANDLER_FOUND);
    }

    /**
     * 上传文件大小超过系统限制
     */
    @ExceptionHandler(value = {MaxUploadSizeExceededException.class})
    public R exception(MaxUploadSizeExceededException e) {
        log.error(e.getMessage(), e);
        return R.err("上传文件大小超过系统限制", RCode.FILE_SIZE_EXCEEDS_LIMIT);
    }

    /**
     * 缺少参数
     */
    @ExceptionHandler(value = {MissingServletRequestParameterException.class})
    public R exception(MissingServletRequestParameterException e) {
        String type = e.getParameterType();
        String name = e.getParameterName();
        return R.err(String.format("参数%s(%s)必须传递", name, type), RCode.PARAMETER_ILLEGAL);
    }

    /**
     * 缺少文件参数
     */
    @ExceptionHandler(value = {MissingServletRequestPartException.class})
    public R exception(MissingServletRequestPartException e) {
        String name = e.getRequestPartName();
        return R.err(String.format("参数(%s)必须传递", name), RCode.PARAMETER_ILLEGAL);
    }

    /**
     * 参数类型不匹配
     */
    @ExceptionHandler(value = {MethodArgumentTypeMismatchException.class})
    public R exception(MethodArgumentTypeMismatchException e) {
        String type = e.getRequiredType().getName();
        String name = e.getName();
        return R.err(String.format("参数%s(%s)类型转换失败", name, type), RCode.PARAMETER_ILLEGAL);
    }

    /**
     * 系统异常
     */
    @ExceptionHandler({Exception.class})
    public R exception(Exception e) {
        log.error(e.getMessage(), e);
        return R.err("服务器错误", RCode.SYSTEM_ERROR);
    }

}
